static inline void pit_load_count(PITChannelState *s, int val)
{
- u32 period;
+ u32 period;
+ PITChannelState *ch0 =
+ ¤t->domain->arch.hvm_domain.pl_time.vpit.channels[0];
+
if (val == 0)
val = 0x10000;
s->count_load_time = hvm_get_clock(s->vcpu);
s->count = val;
period = DIV_ROUND((val * 1000000000ULL), PIT_FREQ);
+ if (s != ch0)
+ return;
+
#ifdef DEBUG_PIT
printk("HVM_PIT: pit-load-counter(%p), count=0x%x, period=%uns mode=%d, load_time=%lld\n",
s,
static uint32_t speaker_ioport_read(void *opaque, uint32_t addr)
{
- int out;
PITState *pit = opaque;
- out = pit_get_out(pit, 2, hvm_get_clock(pit->channels[2].vcpu));
- pit->dummy_refresh_clock ^= 1;
-
- return (pit->speaker_data_on << 1) | pit_get_gate(pit, 2) | (out << 5) |
- (pit->dummy_refresh_clock << 4);
+ int out = pit_get_out(pit, 2, hvm_get_clock(pit->channels[2].vcpu));
+ /* Refresh clock toggles at about 15us. We approximate as 2^14ns. */
+ unsigned int refresh_clock = ((unsigned int)NOW() >> 14) & 1;
+ return ((pit->speaker_data_on << 1) | pit_get_gate(pit, 2) |
+ (out << 5) | refresh_clock << 4);
}
static int handle_speaker_io(ioreq_t *p)
printk("HVM_SPEAKER:wrong SPEAKER IO!\n");
return 1;
}
-
+
if (p->dir == 0) {/* write */
speaker_ioport_write(vpit, p->addr, p->data);
} else if (p->dir == 1) {/* read */
return 1;
}
+
+int pv_pit_handler(int port, int data, int write)
+{
+ ioreq_t ioreq = {
+ .size = 1,
+ .type = IOREQ_TYPE_PIO,
+ .addr = port,
+ .dir = write ? 0 : 1,
+ .data = write ? data : 0,
+ };
+
+ if (port == 0x61)
+ handle_speaker_io(&ioreq);
+ else
+ handle_pit_io(&ioreq);
+
+ return !write ? ioreq.data : 0;
+}
#include <asm/debugger.h>
#include <asm/msr.h>
#include <asm/x86_emulate.h>
+#include <asm/hvm/vpt.h>
/*
* opt_nmi: one of 'ignore', 'dom0', or 'fatal'.
return ioports_access_permitted(v->domain, port, port + bytes - 1);
}
-static inline int guest_inb_okay(
- unsigned int port, struct vcpu *v, struct cpu_user_regs *regs)
-{
- /*
- * Allow read access to port 0x61. Bit 4 oscillates with period 30us, and
- * so it is often used for timing loops in BIOS code. This hack can go
- * away when we have separate read/write permission rangesets.
- * Note that we could emulate bit 4 instead of directly reading port 0x61,
- * but there's not really a good reason to do so.
- */
- return (admin_io_okay(port, 1, v, regs) || (port == 0x61));
-}
+#define guest_inb_okay(_p, _d, _r) admin_io_okay(_p, 1, _d, _r)
#define guest_inw_okay(_p, _d, _r) admin_io_okay(_p, 2, _d, _r)
#define guest_inl_okay(_p, _d, _r) admin_io_okay(_p, 4, _d, _r)
#define guest_outb_okay(_p, _d, _r) admin_io_okay(_p, 1, _d, _r)
switch ( op_bytes )
{
case 1:
- data = (u8)(guest_inb_okay(port, v, regs) ? inb(port) : ~0);
+ /* emulate PIT counter 2 */
+ data = (u8)(guest_inb_okay(port, v, regs) ? inb(port) :
+ ((port == 0x42 || port == 0x43 || port == 0x61) ?
+ pv_pit_handler(port, 0, 0) : ~0));
break;
case 2:
data = (u16)(guest_inw_okay(port, v, regs) ? inw(port) : ~0);
case 1:
if ( guest_outb_okay(port, v, regs) )
outb((u8)data, port);
+ else if ( port == 0x42 || port == 0x43 || port == 0x61 )
+ pv_pit_handler(port, data, 1);
break;
case 2:
if ( guest_outw_okay(port, v, regs) )
case 1:
if ( guest_inb_okay(port, v, regs) )
io_emul(regs);
+ else if ( port == 0x42 || port == 0x43 || port == 0x61 )
+ {
+ regs->eax &= ~0xffUL;
+ regs->eax |= pv_pit_handler(port, 0, 0);
+ }
else
regs->eax |= (u8)~0;
break;
case 1:
if ( guest_outb_okay(port, v, regs) )
io_emul(regs);
+ else if ( port == 0x42 || port == 0x43 || port == 0x61 )
+ pv_pit_handler(port, regs->eax, 1);
break;
case 2:
if ( guest_outw_okay(port, v, regs) )
typedef struct PITState {
PITChannelState channels[3];
int speaker_data_on;
- int dummy_refresh_clock;
} PITState;
#define RTC_SIZE 14
extern struct periodic_time *create_periodic_time(
u32 period, char irq, char one_shot, time_cb *cb, void *data);
extern void destroy_periodic_time(struct periodic_time *pt);
+int pv_pit_handler(int port, int data, int write);
void pit_init(struct vcpu *v, unsigned long cpu_khz);
void rtc_init(struct vcpu *v, int base, int irq);
void rtc_deinit(struct domain *d);